home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 August: Tool Chest / Dev.CD Aug 95 TC / Dev.CD Aug 95 TC.toast / Sample Code / Snippets / Toolbox / Prefs / Pref.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-26  |  16.7 KB  |  559 lines  |  [TEXT/KAHL]

  1. /* Sample System 7 Preferences */
  2.  
  3. /* Apple Developer Technical Support */
  4. /* Greg Robbins                      */
  5.  
  6. /* This application displays a sample preferences dialog
  7.    containing edit text, radio buttons, and a file selection,
  8.    and demonstrates storage of those items in a preferences
  9.    file.
  10.    
  11.    Requires System 7, though most elements (like resource use and
  12.    FindFolder calls) apply to System 6 also.                       */
  13.    
  14. /* Thanks to Godfrey DiGiorgi for advice & assistance. */
  15.  
  16. /* Rev 1.0   7/92 */
  17.  
  18. /* Thanks to Godfrey DiGiorgi for assistance */
  19.  
  20. #ifdef THINK_C
  21.     #include <MacHeaders>
  22. #else
  23.     #include <QuickDraw.h>
  24.     #include <Fonts.h>
  25.     #include <GestaltEqu.h>
  26.     #include <Files.h>
  27.     #include <Resources.h>
  28.     #include <Dialogs.h>
  29.     #include <Menus.h>
  30.     #include <TextEdit.h>
  31.     #include <Windows.h>
  32.     #include <Controls.h>
  33.     #include <Folders.h>
  34.     #include <Errors.h>
  35.     #include <OSUtils.h>
  36.     #include <ToolUtils.h>
  37.     #include <Types.h>
  38.     #include <Memory.h>
  39.     #include <StandardFile.h>
  40.     #include <Aliases.h>
  41. #endif
  42.  
  43. /* preferences dialog ID and items */
  44.  
  45. #define kPrefsDialogID 128
  46. #define kSaveButtonItem 1
  47. #define kCancelButtonItem 2
  48. #define kHighRButtonItem 3
  49. #define kMediumRButtonItem 4
  50. #define kLowRButtonItem 5
  51. #define kSelectButtonItem 6
  52. #define kSelectStatTextItem 7
  53. #define kEditTextItem 8
  54.  
  55. /* prefs file constants */
  56. #define kPrefsNameStrID 128         /* name of prefs file */
  57. #define kPrefsCreatorType '????'
  58. #define kPrefsFileType 'pref'
  59. #define kPrefsResourceType 'Pref'
  60. #define kPrefsResourceID 128
  61. #define kFileAliasResourceID 128    /* resource ID of alias of preferred file */
  62.  
  63. /* misc dialog resource IDs */
  64. #define kGeneralAlertID 129
  65. #define kIntroAlertID 130
  66. #define kErrorStrListID 129        /* STR saying System 7 is required */
  67. #define kSystem7NeededStrIndex 1
  68.  
  69. #define kFinderMessageStrID -16397 /* ID of STR for default finder message */
  70.                                    /* see Inside Mac VI, page 9-22         */
  71. #define kStrType 'STR '
  72.  
  73.  
  74. /* this structure will contain the prefs for the application */
  75. typedef struct {
  76.     long prefsTypeVers;  /* version number of prefs struct                */
  77.     short rButtonShort;  /* radio button number                           */
  78.     short fileAliasID;   /* resource ID for saving alias of selected file */
  79.     Str255 editStr;      /* string from edit text box                     */
  80. } PrefsType;
  81.  
  82. /* these constants may be stored in the fields of the prefs struct */
  83. #define kCurrentPrefsTypeVers 1
  84. #define kNoAliasResourceID -1
  85.  
  86. /* function prototypes */
  87.  
  88. void main (void);
  89. short OpenPrefsResFile(SignedByte prefsPerm, Boolean createFlag);
  90. Boolean DoPrefsDialog(PrefsType * thePrefsType, AliasHandle * fileAliasHandlePtr);
  91.  
  92. void GetOrGeneratePrefs(PrefsType * thePrefsTypePtr, long versWantedLong, 
  93.     AliasHandle * fileAliasHandlePtr);
  94. OSErr SavePrefs(PrefsType * thePrefsTypePtr, AliasHandle * fileAliasHandlePtr);
  95.  
  96. void SetPrefRadioButton(DialogPtr prefDialogPtr, short buttonItemNum,Boolean setFlag);
  97. void SetPrefRadioButtonTrio(DialogPtr prefDialogPtr, short item);
  98. void SetPrefText(DialogPtr prefDialogPtr, short textItemNum, Str255 setStr);
  99.  
  100. OSErr GetPrefsName(Str255 prefName);
  101.  
  102. /*
  103.  *
  104.  *   main program
  105.  *
  106.  */
  107.  
  108. void main () 
  109. {
  110.     PrefsType currPrefs;                  /* prefs structure  */
  111.     AliasHandle fileAliasHandle = nil;    /* "preferred" file */
  112.  
  113.     Boolean prefsChangedFlag;
  114.     
  115.     long gestAliasResponse, gestStdFileResponse, gestFSResponse;
  116.     short tempResRefNum;
  117.     Str255 errStr;
  118.     OSErr retCode;
  119.     
  120.     /* standard toolbox initialization */
  121.     InitGraf(&qd.thePort);
  122.     InitFonts();
  123.     InitWindows();
  124.     InitMenus();
  125.     TEInit();
  126.     InitDialogs(nil);
  127.     InitCursor();
  128.     
  129.     /* Ensure that appropriate facilities are available so I don't crash
  130.     
  131.        Think & MPW glue handles calls to Gestalt and FindFolder under 6.x so
  132.        those are always available, but the Alias Manager, new Standard File,
  133.        and FSSpecs must be present as indicated by Gestalt */
  134.     
  135.     retCode = Gestalt(gestaltAliasMgrAttr, &gestAliasResponse);
  136.     if (retCode == noErr) retCode = Gestalt(gestaltFSAttr, &gestFSResponse);
  137.     if (retCode == noErr)
  138.         retCode = Gestalt(gestaltStandardFileAttr, &gestStdFileResponse);
  139.     
  140.     if (retCode != noErr ||
  141.         (gestAliasResponse & (1 << gestaltAliasMgrPresent)) == 0 ||
  142.         (gestFSResponse & (1 << gestaltHasFSSpecCalls)) == 0 ||
  143.         (gestStdFileResponse & (1 << gestaltStandardFile58)) == 0) {
  144.     
  145.         /* display a dialog saying System 7 is needed, then bail out */
  146.         
  147.         GetIndString(errStr, kErrorStrListID, kSystem7NeededStrIndex);
  148.         ParamText(errStr, nil, nil, nil);
  149.         (void) StopAlert(kGeneralAlertID, nil);
  150.         ExitToShell();
  151.     }
  152.         
  153.     /* if there is no preference file already, display the introductory dialog   */
  154.     
  155.     /* this is a bit lazy: it just tries to open the file, and if that succeeds,
  156.      * the file is immediately closed                                           
  157.      */
  158.     
  159.     tempResRefNum = OpenPrefsResFile(fsRdPerm, false);
  160.     
  161.     if (tempResRefNum == -1)
  162.         (void) Alert(kIntroAlertID, (ProcPtr) nil); /* intro dialog */
  163.     else CloseResFile(tempResRefNum);
  164.         
  165.         
  166.     /* Three steps for the preferences dialog:
  167.      * - get the old preference settings from the prefs file,
  168.      *   or fill in the prefs record with defaults
  169.      * - display the dialog
  170.      * - if the user made changes, save the new preferences
  171.      */
  172.      
  173.     GetOrGeneratePrefs(&currPrefs, kCurrentPrefsTypeVers, &fileAliasHandle);
  174.     
  175.     prefsChangedFlag = DoPrefsDialog(&currPrefs, &fileAliasHandle);
  176.     
  177.     if (prefsChangedFlag) retCode = SavePrefs(&currPrefs, &fileAliasHandle);
  178. }
  179.  
  180.  
  181. Boolean DoPrefsDialog(PrefsType * thePrefsTypePtr, AliasHandle * fileAliasHandlePtr)
  182. /* display the prefs dialog; returns true if change made to prefs record */
  183. {
  184.     GrafPtr savePort;
  185.     DialogPtr prefDialogPtr;
  186.     short item;
  187.     Boolean doneFlag = false;
  188.     OSErr retCode;
  189.  
  190.     /* keep the new preference settings only in local variables
  191.        in case the user cancels */
  192.        
  193.     PrefsType newPrefsType;
  194.     AliasHandle newFileAliasHandle;
  195.  
  196.     StandardFileReply mySFR;
  197.     SFTypeList mySFTypeList;
  198.  
  199.     short itemType;
  200.     Handle itemHandle;
  201.     Rect itemRect;
  202.     
  203.     FSSpec tempFSSpec;
  204.     Boolean changedFlag;
  205.  
  206.  
  207.     /* the old settings are our starting point, so copy them */
  208.     newPrefsType = *thePrefsTypePtr;
  209.     newFileAliasHandle = *fileAliasHandlePtr;
  210.     
  211.     /* throw up the dialog */
  212.     prefDialogPtr = GetNewDialog(kPrefsDialogID, nil, (WindowPtr) -1);
  213.     if (prefDialogPtr == nil) ExitToShell();  /* hurtin’ bad */
  214.     
  215.     /* set up the dialog to the initial values */
  216.     SetPrefRadioButtonTrio(prefDialogPtr, thePrefsTypePtr->rButtonShort);
  217.     SetPrefText(prefDialogPtr, kEditTextItem, thePrefsTypePtr->editStr);
  218.     SelIText(prefDialogPtr, kEditTextItem, 0, 32767);
  219.     
  220.     /* set the initial file name by resolving the alias */
  221.     retCode = ResolveAlias(nil, newFileAliasHandle, &tempFSSpec, &changedFlag);
  222.     if (retCode == noErr)
  223.         SetPrefText(prefDialogPtr, kSelectStatTextItem, tempFSSpec.name);
  224.     else SetPrefText(prefDialogPtr, kSelectStatTextItem, "\p");
  225.     
  226.  
  227.     do {
  228.         GetPort(&savePort);
  229.         SetPort(prefDialogPtr);
  230.         ModalDialog(nil, &item);
  231.         switch(item) {
  232.         
  233.             case kSaveButtonItem:
  234.                 /* user wants to save the current or new settings */
  235.                 
  236.                 /* copy the editText string into the new prefs record */
  237.                 GetDItem(prefDialogPtr, kEditTextItem, 
  238.                     &itemType, &itemHandle, &itemRect);
  239.                 GetIText(itemHandle, newPrefsType.editStr);
  240.                                 
  241.                 /* replace the old record with the new record */
  242.                 *thePrefsTypePtr = newPrefsType;
  243.                 
  244.                 /* if there is a new file alias, dispose of the old one and
  245.                    copy the new handle */
  246.                    
  247.                 if (*fileAliasHandlePtr != newFileAliasHandle) {
  248.                     if (*fileAliasHandlePtr != nil)
  249.                         DisposHandle((Handle)  *fileAliasHandlePtr);
  250.                     *fileAliasHandlePtr = newFileAliasHandle;
  251.                 }
  252.                 doneFlag = true;
  253.                 break;
  254.                 
  255.             case kCancelButtonItem:
  256.                 /* abandon the new preferences structure */
  257.                 doneFlag = true;
  258.                 
  259.                 /* if an alias was allocated, dispose of it */
  260.                 if (newFileAliasHandle != *fileAliasHandlePtr &&
  261.                     newFileAliasHandle != nil)
  262.                     DisposHandle((Handle) newFileAliasHandle);
  263.                 break;
  264.                 
  265.             case kHighRButtonItem:
  266.             case kMediumRButtonItem:
  267.             case kLowRButtonItem:
  268.                 /* handle a radio button click */
  269.                 SetPrefRadioButtonTrio(prefDialogPtr, item);
  270.                 newPrefsType.rButtonShort = item;
  271.                 break;
  272.                 
  273.             case kSelectButtonItem:
  274.                 /* select a file the usual way */
  275.                 StandardGetFile(nil, -1, mySFTypeList, &mySFR);
  276.                 
  277.                 if (mySFR.sfGood) {
  278.                 
  279.                     /* make an alias to the selected file */
  280.                     
  281.                     newPrefsType.fileAliasID = kFileAliasResourceID;
  282.  
  283.                     /* if an alias was allocated, dispose of it */
  284.                     if (newFileAliasHandle != *fileAliasHandlePtr &&
  285.                         newFileAliasHandle != nil)
  286.                         DisposHandle((Handle) newFileAliasHandle);
  287.  
  288.                     retCode = NewAlias(nil, &mySFR.sfFile, &newFileAliasHandle);
  289.                     
  290.                     if (retCode != noErr) {
  291.                         /* really should display a warning or something */
  292.                         SysBeep(10);
  293.                         newFileAliasHandle = nil;
  294.                         *mySFR.sfFile.name = 0;
  295.                         newPrefsType.fileAliasID = kNoAliasResourceID;
  296.                     }
  297.                     
  298.                     /* display the selected file's name in the dialog */
  299.                     
  300.                     SetPrefText(prefDialogPtr, kSelectStatTextItem, mySFR.sfFile.name);
  301.                 }
  302.         }
  303.     } while (!doneFlag);
  304.     
  305.     DisposDialog(prefDialogPtr);
  306.     
  307.     SetPort(savePort);
  308.  
  309.     /* if the user clicked Save, return true */
  310.     return (item == kSaveButtonItem);
  311. }
  312.  
  313. void SetPrefRadioButtonTrio(DialogPtr prefDialogPtr, short item)
  314. /* set one radio button item in the prefs dialog, clear the others */
  315. {
  316.     SetPrefRadioButton(prefDialogPtr, kLowRButtonItem,(item == kLowRButtonItem));
  317.     SetPrefRadioButton(prefDialogPtr, kMediumRButtonItem,(item == kMediumRButtonItem));
  318.     SetPrefRadioButton(prefDialogPtr, kHighRButtonItem,(item == kHighRButtonItem));                 
  319. }
  320.  
  321. void SetPrefRadioButton(DialogPtr prefDialogPtr, short buttonItemNum, Boolean setFlag)
  322. /* set radio button number buttonItemNum in the prefs dialog if setFlag is true */
  323. {
  324.     short itemType;
  325.     Handle itemHandle;
  326.     Rect itemRect;
  327.  
  328.     GetDItem(prefDialogPtr, buttonItemNum, &itemType, &itemHandle, &itemRect);
  329.     SetCtlValue((ControlHandle) itemHandle, (setFlag ? 1 : 0));
  330. }
  331.  
  332. void SetPrefText(DialogPtr prefDialogPtr, short textItemNum, Str255 setStr)
  333. /* set the text item in the dialog to setStr */
  334. {
  335.     short itemType;
  336.     Handle itemHandle;
  337.     Rect itemRect;
  338.  
  339.     GetDItem(prefDialogPtr, textItemNum, &itemType, &itemHandle, &itemRect);
  340.     SetIText(itemHandle, setStr);
  341. }
  342.  
  343.  
  344. void GetOrGeneratePrefs(PrefsType * thePrefsTypePtr, long wantedVers,
  345.     AliasHandle * fileAliasHandlePtr)
  346. /* fill in the prefs structure from the resource file or from scratch */
  347. {
  348.     short prefsResRefNum;
  349.     Handle tempHandle;
  350.     
  351.     Str255 defaultStr = "\pDefault Edit Text";
  352.     
  353.     /* initialize prefs structure in case we can't get a valid set */
  354.     thePrefsTypePtr->prefsTypeVers = kCurrentPrefsTypeVers;
  355.     thePrefsTypePtr->rButtonShort = kMediumRButtonItem;
  356.     thePrefsTypePtr->fileAliasID = kNoAliasResourceID;
  357.     BlockMove(defaultStr, thePrefsTypePtr->editStr, *defaultStr+1);
  358.     *fileAliasHandlePtr = nil;
  359.     
  360.     /* open (but don't create) the prefs file */
  361.     prefsResRefNum = OpenPrefsResFile(fsRdPerm, false);
  362.     if (prefsResRefNum != -1) {
  363.     
  364.         /* file opened successfully, get the prefs resource */
  365.         tempHandle = Get1Resource(kPrefsResourceType, kPrefsResourceID);
  366.         
  367.         /* if the resource is there and it's the right size and version, copy it */
  368.         /* (in C these can be combined with &&, but don't use AND in Pascal)     */
  369.         
  370.         if (tempHandle != nil)
  371.             if (GetHandleSize(tempHandle) == sizeof(PrefsType)) 
  372.                 if ((*(PrefsType *)*tempHandle).prefsTypeVers == wantedVers) {
  373.                 
  374.                     /* copy the prefs struct */
  375.                     *thePrefsTypePtr = *(PrefsType *)*tempHandle;
  376.                     
  377.                     /* if there's also an alias, get and detach that, too */
  378.                     if (thePrefsTypePtr->fileAliasID != kNoAliasResourceID) {
  379.                     
  380.                         /* don't orphan a previous alias handle */
  381.                         if (*fileAliasHandlePtr != nil)
  382.                             DisposHandle((Handle) *fileAliasHandlePtr);
  383.                             
  384.                         *fileAliasHandlePtr = (AliasHandle)
  385.                             Get1Resource(rAliasType, thePrefsTypePtr->fileAliasID);
  386.                         
  387.                         if (*fileAliasHandlePtr != nil)
  388.                             DetachResource((Handle) *fileAliasHandlePtr);
  389.                     }
  390.                 }
  391.         /* release the pref resource and close the file */
  392.         CloseResFile(prefsResRefNum);
  393.     }
  394. }
  395.  
  396. OSErr SavePrefs(PrefsType * thePrefsTypePtr, AliasHandle * fileAliasHandlePtr)
  397. /* save the prefs structure in the prefs resource file */
  398. {
  399.     OSErr retCode;
  400.     short prefsResRefNum;
  401.     Handle prefHandle, alisHandle, finderMessageHandle;
  402.     
  403.     /* open (and, if necessary, create) the prefs file */
  404.     prefsResRefNum = OpenPrefsResFile(fsRdWrPerm, true);
  405.     if (prefsResRefNum != -1) {
  406.     
  407.         /* file opened successfully, get the prefs resource */
  408.         prefHandle = Get1Resource(kPrefsResourceType, kPrefsResourceID);
  409.         
  410.         if (prefHandle == nil) {
  411.         
  412.             /* create a new resource */
  413.             prefHandle = NewHandle(sizeof(PrefsType));
  414.             if (prefHandle != nil) {
  415.             
  416.                 /* copy the prefs struct into the handle
  417.                    and make it into a resource */
  418.                 
  419.                 *(PrefsType *)*prefHandle = *thePrefsTypePtr;
  420.                 AddResource(prefHandle, kPrefsResourceType, kPrefsResourceID, 
  421.                     "\pPrefsType");
  422.                 retCode = ResError();
  423.                 if (retCode != noErr) DisposHandle(prefHandle);
  424.             } 
  425.             
  426.             else retCode = MemError(); /* NewHandle failed */
  427.         }
  428.         
  429.         else {  /* prefHandle != nil */
  430.         
  431.             /* update the existing resource */
  432.             SetHandleSize(prefHandle, sizeof(PrefsType));
  433.             retCode = MemError();
  434.             if (retCode == noErr) {
  435.  
  436.                 /* copy the prefs struct into the handle and tell the rsrc manager */
  437.                 *(PrefsType *)*prefHandle = *thePrefsTypePtr;
  438.                 ChangedResource(prefHandle);
  439.             }
  440.         }
  441.         
  442.         if (retCode == noErr) {
  443.             /* now, get rid of the old fileAlias and, if the fileAliasID field
  444.                of the prefs struct indicates that there is a new one, add it to
  445.                the resource file */
  446.                
  447.             /* remove any old file alias */
  448.             alisHandle = Get1Resource(rAliasType, thePrefsTypePtr->fileAliasID);
  449.             if (alisHandle != nil) {
  450.                 RmveResource(alisHandle);
  451.                 DisposHandle(alisHandle);
  452.             }
  453.             
  454.             /* if appropriate, add the current alias to the resource file */
  455.             
  456.             if (thePrefsTypePtr->fileAliasID != kNoAliasResourceID &&
  457.                 *fileAliasHandlePtr != nil) {
  458.                 
  459.                 AddResource((Handle) *fileAliasHandlePtr, rAliasType, 
  460.                     thePrefsTypePtr->fileAliasID, "\pfile alias");
  461.                     
  462.                 /* keep the alias available */
  463.                 DetachResource((Handle) *fileAliasHandlePtr);
  464.             }
  465.             
  466.             /* add the message to be displayed if the user tries
  467.                to open the prefs file in the Finder (but don't add it
  468.                if it's already in the preferences file) */
  469.                
  470.             finderMessageHandle = (Handle) GetString(kFinderMessageStrID);
  471.             if (finderMessageHandle != nil &&
  472.                 HomeResFile((Handle) finderMessageHandle) != prefsResRefNum) {
  473.             
  474.                 /* copy the resource into the prefs file */
  475.                 DetachResource(finderMessageHandle);
  476.                 AddResource(finderMessageHandle, kStrType, kFinderMessageStrID,
  477.                     "\pFinder message");
  478.                     
  479.                 /* if AddResource failed, dispose of the handle */
  480.                 retCode = ResError();
  481.                 if (retCode != noErr) DisposHandle(finderMessageHandle);
  482.             }
  483.         }
  484.         
  485.         /* update and close the preference resource file, 
  486.            releasing its resources from memory */
  487.         CloseResFile(prefsResRefNum);
  488.     }
  489.     
  490.     else {
  491.         /* couldn't open the res file */
  492.         retCode = ResError();
  493.         if (retCode == noErr) retCode = resFNotFound;
  494.     }
  495.     
  496.     return retCode;
  497. }
  498.  
  499. short OpenPrefsResFile(SignedByte prefsPerm, Boolean createFlag)
  500. /* open the preferences file with the given permission; if createFlag is set,
  501.    create a preferences file if necessary */
  502. {
  503.     OSErr retCode;
  504.     short prefsVRefNum;
  505.     long prefsDirID;
  506.     Str255 prefsNameStr;
  507.     FSSpec prefsFSSpec;
  508.     short prefsResRefNum = -1;
  509.     
  510.     /* get the name of the prefs file */
  511.     retCode = GetPrefsName(prefsNameStr);
  512.     if (retCode == noErr) {
  513.     
  514.         /* Use FindFolder to locate the Preferences folder.
  515.         
  516.            MPW 3.2 and some other compilers provide the
  517.            glue necessary to use FindFolder (and Gestalt) safely
  518.            under System 6 and earlier.  Under System 6, the dirID
  519.            returned for the Preferences folder request will be
  520.            the System folder's dirID */
  521.  
  522.         retCode = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
  523.             &prefsVRefNum, &prefsDirID);
  524.         if (retCode == noErr) {
  525.         
  526.             /* make a file spec for the prefs file */
  527.             
  528.             retCode = FSMakeFSSpec(prefsVRefNum, prefsDirID, prefsNameStr,
  529.                 &prefsFSSpec);
  530.                 
  531.             if (retCode == fnfErr && createFlag) {
  532.                 /* prefs file doesn't already exist, so create it */
  533.                 FSpCreateResFile(&prefsFSSpec, kPrefsCreatorType, kPrefsFileType,
  534.                     smSystemScript);
  535.                 retCode = ResError();
  536.             }
  537.             
  538.             /* open the prefs file */
  539.             if (retCode == noErr) {
  540.                 prefsResRefNum = FSpOpenResFile(&prefsFSSpec, prefsPerm);
  541.             }
  542.         }
  543.     }
  544.     return prefsResRefNum;
  545. }
  546.  
  547. OSErr GetPrefsName (StringPtr prefName)
  548. /* get the name of the preferences file from a resource in the application */
  549. {
  550.     StringHandle prefStrHandle;
  551.     OSErr retCode = noErr;
  552.     
  553.     prefStrHandle = GetString(kPrefsNameStrID);
  554.     if (prefStrHandle != nil) 
  555.         BlockMove(*prefStrHandle, prefName, (Size) (**prefStrHandle) + 1);
  556.     else retCode = resNotFound;
  557.     
  558.     return retCode;
  559. }